#pragma once

#include <time.h>

#include <chrono>
#include <iomanip>
#include <sstream>
#include <string>

/**
 * @brief
 *
 * @param time : 2022-08-02 12:23:23
 * @return time_t
 */
__attribute__((unused)) static time_t convert_time(const std::string time) {
    struct tm tt;

    sscanf(time.c_str(),
           "%d-%d-%d %d:%d:%d",
           &tt.tm_year,
           &tt.tm_mon,
           &tt.tm_mday,
           &tt.tm_hour,
           &tt.tm_min,
           &tt.tm_sec);
    tt.tm_year -= 1900;
    tt.tm_mon -= 1;
    tt.tm_isdst = -1;

    return mktime(&tt);
}

__attribute__((unused)) static struct tm t2calendar(time_t t) {
    struct tm _tm;
    localtime_r(&t, &_tm);
    _tm.tm_year += 1900;
    _tm.tm_mon += 1;
    return _tm;
}

__attribute__((unused)) static uint64_t get_millisecond() {
    auto time_now = std::chrono::system_clock::now();
    auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        time_now.time_since_epoch());
    return duration_in_ms.count();
}

__attribute__((unused)) static uint64_t get_second() {
    auto time_now = std::chrono::system_clock::now();
    auto duration_in_s = std::chrono::duration_cast<std::chrono::seconds>(
        time_now.time_since_epoch());
    return duration_in_s.count();
}

__attribute__((unused)) static std::string serialize_timepoint(
    const std::chrono::system_clock::time_point& time) {
    static const std::string format = "%Y-%m-%d %H:%M:%S.";
    std::stringstream ss;

    auto t_since_epoch = time.time_since_epoch();

    // std :: cout << t_since_epoch.count() << std::endl;
    std::time_t tt = std::chrono::system_clock::to_time_t(time);
    std::tm tm = *std::localtime(&tt);  // GMT (UTC)

    ss << std::put_time(&tm, format.c_str());
    auto milliseconds =
        std::chrono::duration_cast<std::chrono::milliseconds>(t_since_epoch) %
        1000;
    auto microseconds =
        std::chrono::duration_cast<std::chrono::microseconds>(t_since_epoch) %
        1000;

    ss << std::setw(3) << std::setfill('0') << milliseconds.count();
    ss << std::setw(3) << std::setfill('0') << microseconds.count();
    return ss.str();
}

__attribute__((unused)) static std::string serialize_timestamp_s(
    uint64_t second) {
    std::chrono::seconds dur(second);
    std::chrono::time_point<std::chrono::system_clock> tp(dur);
    return serialize_timepoint(tp);
}

__attribute__((unused)) static std::string serialize_timestamp_ms(
    uint64_t milliseconds) {
    std::chrono::milliseconds dur(milliseconds);
    std::chrono::time_point<std::chrono::system_clock> tp(dur);
    return serialize_timepoint(tp);
}

__attribute__((unused)) static std::string serialize_timestamp_us(
    uint64_t microseconds) {
    std::chrono::microseconds dur(microseconds);
    std::chrono::time_point<std::chrono::system_clock> tp(dur);
    return serialize_timepoint(tp);
}

__attribute__((unused)) static time_t mktime_from_string(
    const std::string& tmstr) {
    // tmstr: 2023-04-18-14-58-29
    int year = -1, month = -1, day = -1, hour = -1, min = -1, sec = -1;
    sscanf(tmstr.c_str(),
           "%d-%d-%d-%d-%d-%d",
           &year,
           &month,
           &day,
           &hour,
           &min,
           &sec);

    struct tm _tm;
    _tm.tm_year = year - 1900;
    _tm.tm_mon = month - 1;  // 0~11
    _tm.tm_mday = day;       // 1~31
    _tm.tm_hour = hour;      // 0~23
    _tm.tm_min = min;        // 0-59
    _tm.tm_sec = sec;        // 0-59
    _tm.tm_isdst = 0;
    return mktime(&_tm);
}